#ifndef GAMEENGINE_GEOMETRY_CIRCLE2D_HPP
#define GAMEENGINE_GEOMETRY_CIRCLE2D_HPP

#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

//=================================================================================
// Implementation of the circle class

//! Default constructor
/*!
 @note Class T should have a default constructor
 */
template <class T>
Circle2d<T>::Circle2d() {}

//! Constructor from a center and a radius
/*!
 @param center The center of the circle
 @param radius The radius of the circle
 @note Class T should have a copy constructor
 */
template <class T>
Circle2d<T>::Circle2d(const Point2d<T>& center, const T& radius) :
	center_(center), radius_(radius) {}

//! Copy constructor
/*!
 @param rhs The circle to copy
 @note Class T should have a default constructor
 @note Class T should have an assignment operator
 */
template <class T>
Circle2d<T>::Circle2d(const Circle2d& rhs) : center_(rhs.center_), radius_(rhs.radius_) {}

//! Destructor
/*!
 @note The destructor of class T should be public
 */
template <class T>
Circle2d<T>::~Circle2d() {}

//! Assignment operator
/*!
 @param rhs The circle to copy
 @note Class T should have an assignment operator
 */
template <class T>
Circle2d<T>& Circle2d<T>::operator=(const Circle2d& rhs) {
	if ( &rhs != this ) {
		center_ = rhs.center_;
		radius_ = rhs.radius_;
	}
	return *this;
}

//! Accessor to the center of the circle (read-write)
/*!
 @return A reference to the center of the circle
 */
template <class T>
Point2d<T>& Circle2d<T>::center() {
	return center_;
}

//! Accessor to the center of the circle (read only)
/*!
 @return A constant reference to the center of the circle
 */
template <class T>
const Point2d<T>& Circle2d<T>::center() const {
	return center_;
}

//! Accessor to the radius of the circle (read-write)
/*!
 @return A reference to the radius of the circle
 */
template <class T>
T& Circle2d<T>::radius() {
	return radius_;
}

//! Accessor to the radius of the circle (read only)
/*!
 @return A constant reference to the radius of the circle
 */
template <class T>
const T& Circle2d<T>::radius() const {
	return radius_;
}

//! Compute the diameter of the circle
/*!
 @return The diameter of the circle, i.e., 2*radius
 */
template <class T>
T Circle2d<T>::diameter() const {
	return (T)(2.0*radius_);
}

//! Compute the area of the circle
/*!
 @return The area of the circle, i.e., pi*radius*radius
 */
template <class T>
T Circle2d<T>::area() const {
	return (T)(PI*radius_*radius_);
}

//! Test wether a point is inside the circle
/*!
 @param rhs The point to test for inclusion
 @param strict If set to true, a point located exactly on the perimeter of
			   the circle is not considered to belong to the circle
 @return True if the point is inside the circle, false otherwise
 */
template <class T>
bool Circle2d<T>::inside(const Point2d<T>& rhs, bool strict) const {
	// Get the square distance between the points
	T square_dist = center_.get_square_distance(rhs);

	// Compare with the square radius to test point inclusion
	if ( strict ) return ( square_dist < (radius_*radius_) );
	return ( square_dist <= (radius_*radius_) );
}

//! Compute the intersection between the line and the circle
/*!
 @param rhs The line to intersect with the circle
 @param coordinate1 The parametric coordinate of the first intersection between
                    the circle and the line, if both do intersect. To retrieve
					the intersection point, call rhs.point(coordinate1)
 @param coordinate2 The parametric coordinate of the second intersection between
                    the circle and the line, if both do intersect. To retrieve
					the intersection point, call rhs.point(coordinate2)
 @param type If not nil, this parameter receives the type of intersection
             between the circle and the line. The intersection type can be:<BR>
			 NoIntersection if the line does not intersect the circle,<BR>
			 TangentIntersection if the line is tangent to the circle,<BR>
			 Intersection if the line fully intersects the circle,<BR>
			 DegenerateIntersection if the line is degenerate (i.e. that the
			 first and second points of the line are the same).
 @return The number of intersections between the line and the circle. Returned
         value can be 0 (NoIntersection or DegenerateIntersection), 1
		 (TangentIntersection) or 2 (Intersection)
 @note Type T should have an implicit conversion to type double
 */
template <class T>
int Circle2d<T>::intersection(const Line2d<T>& rhs, T& coordinate1, T& coordinate2, IntersectionType::Type* type) const {
	// Check that line is not degenerate
	T square_length = rhs.p1().get_square_distance(rhs.p2());
	if ( square_length <= 0 ) {
		if ( type ) *type = IntersectionType::DegenerateIntersection;
		return 0;
	}

	// Get closest point distance
	T param = rhs.closest_point(center_);
	T dist = rhs.point(param).get_square_distance(center_);

	// If it is superior to the radius, there is no intersection
	if ( dist > (radius_*radius_) ) {
		coordinate1 = param;
		coordinate2 = param;
		if ( type ) *type = IntersectionType::NoIntersection;
		return 0;
	}

	// If it is equal to the radius, there is one tangent intersection
	if ( dist == (radius_*radius_) ) {
		coordinate1 = param;
		coordinate2 = param;
		if ( type ) *type = IntersectionType::TangentIntersection;
		return 1;
	}

	// Otherwise, there are two intersections
	T diff = (T)(sqrt((double)(radius_*radius_-dist)) / rhs.p1().get_distance(rhs.p2()));
	coordinate1 = param - diff;
	coordinate2 = param + diff;
	if ( type ) *type = IntersectionType::Intersection;
	return 2;
}

//! Compute the closest point along the circle
/*!
 @return The closest point along the circle
 @param rhs The point to project on the circle
 */
template <class T>
Point2d<T> Circle2d<T>::closest_point(const Point2d<T>& rhs) const {
	// Project point on the circle plane
	T coordinate1, coordinate2;
	Line2d<T> line(rhs, center_);
	IntersectionType::Type type;
	int nb = intersection( line, coordinate1, coordinate2, &type );

	// No intersection => line was degenerate
	if ( nb == 0 ) return center_;

	// Line cuts the circle
	coordinate1 = ( ( abs(coordinate1) < abs(coordinate2) ) ? coordinate1 : coordinate2 );
	return line.point(coordinate1);
}

//! Compute the point at the given circle angle
/*!
 @return The point at the given angle of the circle
 */
template <class T>
Point2d<T> Circle2d<T>::point(const Angle<T>& angle) const {
	return Point2d<T>(
		center_.x() + radius_*cos(angle.rad()),
		center_.y() + radius_*sin(angle.rad()));
}

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
